﻿using System;

using System.Collections.Generic;
using System.Text;

namespace Silvermoon.Shapes
{
    /// <summary>
    /// Collection of shapes as single linked list.
    /// </summary>
    public class ShapeCollection:IEnumerable<Shape>
    {
        /// <summary>
        /// Gets the first shape in the collection.
        /// </summary>
        public Shape First { get; private set; }

        /// <summary>
        /// Gets the last shape in the collection.
        /// </summary>
        public Shape Last { get; private set; }

        /// <summary>
        /// Gets whether the collection is empty.
        /// </summary>
        public bool IsEmpty
        {
            get { return First == null; }
        }

        /// <summary>
        /// Gets whether the collection has more than one shape.
        /// </summary>
        public bool HasMany
        {
            get { return First != null && First != Last; }
        }

        /// <summary>
        /// Adds a shape at the end of the collection.
        /// </summary>
        /// <param name="shape">Shape to add.</param>
        public void AddLast(Shape shape)
        {
            if (shape != null)
            {
                shape.NextShape = null;

                Shape last = Last;
                if (last == null)
                {
                    Last = First = shape;
                }
                else
                {
                    last.NextShape = shape;
                    Last = shape;
                }
            }
        }

        /// <summary>
        /// Adds a shape at the beginning of the collection.
        /// </summary>
        /// <param name="shape">Shape to add.</param>
        public void AddFirst(Shape shape)
        {
            if (shape != null)
            {
                Shape first = First;
                if (first == null)
                {
                    shape.NextShape = null;
                    First = Last = shape;
                }
                else
                {
                    shape.NextShape = first;
                    First = shape;
                }
            }
        }

        /// <summary>
        /// Removes shape from the collection.
        /// </summary>
        /// <param name="shape">Shape to remove.</param>
        public void Remove(Shape shape)
        {
            if (shape == null) return;
            Shape prev = First;
            if (prev == null) return;
            if (prev == shape)
            {
                First = shape.NextShape;
                shape.NextShape = null;
                if (shape == Last) Last = null;
            }
            else
            {
                Shape current = prev.NextShape;
                while (current != null)
                {
                    if (current == shape)
                    {
                        prev.NextShape = shape.NextShape;
                        if (shape == Last) Last = prev;
                        return;
                    }
                    prev = current;
                    current = current.NextShape;
                }
            }
        }

        /// <summary>
        /// Appends a shape after another.
        /// </summary>
        /// <param name="fromShape">Shape from where to append.</param>
        /// <param name="shapeToAppend">Shape to append.</param>
        public void Append(Shape fromShape, Shape shapeToAppend)
        {
            if (fromShape == null) AddFirst(shapeToAppend);
            else
            {
                if (fromShape.NextShape == null)
                {
                    Last = shapeToAppend;
                    fromShape.NextShape = shapeToAppend;
                }
                else
                {
                    Shape next = fromShape.NextShape;
                    shapeToAppend.NextShape = next;
                    fromShape.NextShape = shapeToAppend;
                    if (next == Last) Last = shapeToAppend;
                }
            }
        }

        /// <summary>
        /// Prepends a shape before another.
        /// </summary>
        /// <param name="fromShape">Shape from where to prepend.</param>
        /// <param name="shapeToPrepend">Shape to prepend.</param>
        public void Prepend(Shape fromShape, Shape shapeToPrepend)
        {
            if (fromShape == null)
            {
                AddFirst(shapeToPrepend);
                return;
            }
            if (fromShape == First)
            {
                First = shapeToPrepend;
                shapeToPrepend.NextShape = fromShape;
            }
            else
            {
                Shape first = First;
                while (first != null)
                {
                    Shape next = first.NextShape;
                    if (next == fromShape)
                    {
                        shapeToPrepend.NextShape = fromShape;
                        first.NextShape = shapeToPrepend;
                        return;
                    }
                    first = next;
                }
            }
        }

        /// <summary>
        /// Clears the collection.
        /// </summary>
        public void Clear()
        {
            //Shape s = First;
            //while (s != null)
            //{
            //    Shape n = s.NextShape;
            //    s.NextShape = null;
            //    s = n;
            //}
            First = Last = null;
        }


        /// <summary>
        /// Adds a range of shapes at the end of the collection.
        /// </summary>
        /// <param name="shapes">Shapes to add.</param>
        public void AddRange(params Shape[] shapes)
        {
            foreach (Shape shape in shapes) AddLast(shape);
        }

        /// <summary>
        /// Replaces a shape by another.
        /// </summary>
        /// <param name="oldShape">Shape to be replaced.</param>
        /// <param name="newShape">Replacing shape.</param>
        public void Replace(Shape oldShape, Shape newShape)
        {
            newShape.NextShape = oldShape.NextShape;

            if (oldShape == First)
            {
                First = newShape;
            }
            else
            {
                Shape first = First;
                while (first != null)
                {
                    Shape next = first.NextShape;
                    if (next == oldShape)
                    {
                        first.NextShape = newShape;
                        break;
                    }
                    first = next;
                }
            }
        }


        #region IEnumerable<Shape> Members

        public IEnumerator<Shape> GetEnumerator()
        {
            return new Enumerator(this);
        }

        class Enumerator : IEnumerator<Shape>
        {
            private ShapeCollection collection;
            private Shape current;
            private bool start;

            public Enumerator(ShapeCollection collection)
                : base()
            {
                this.collection = collection;
                Reset();
            }

            #region IEnumerator<Shape> Members

            public Shape Current
            {
                get { return current; }
            }

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
            }

            #endregion

            #region IEnumerator Members

            object System.Collections.IEnumerator.Current
            {
                get { return current; }
            }

            public bool MoveNext()
            {
                if (!start)
                {
                    current = collection.First;
                    start = true;
                }
                else
                {
                    current = current != null ? current.NextShape : null;
                }
                return current != null;
            }

            public void Reset()
            {
                start = false;
                current = collection.First;
            }

            #endregion
        }


        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new Enumerator(this);
        }

        #endregion
    }
}
